import com.intellij.database.model.DasTable
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil

import java.text.SimpleDateFormat

/*
 * Available context bindings:
 *   SELECTION   Iterable<DasObject>
 *   PROJECT     project
 *   FILES       files helper
 */

baseBeanName = ""

typeMapping = [
        (~/(?i)tinyint|bool|bit/)                : "Boolean",
        (~/(?i)smallint|mediumint/)              : "Integer",
        (~/(?i)bigint/)                          : "Long",
        (~/(?i)int/)                             : "Integer",
        (~/(?i)float|double|decimal|real/)       : "Double",
        (~/(?i)datetime/)                        : "LocalDateTime",
        (~/(?i)timestamp/)                       : "Date",
        (~/(?i)date/)                            : "LocalDate",
        (~/(?i)time/)                            : "LocalTime",
        (~/(?i)blob|binary|bfile|clob|raw|image/): "InputStream",
        (~/(?i)/)                                : "String"
]

FILES.chooseDirectoryAndSave("Choose directory", "Choose where to store generated files") { dir ->
    SELECTION.filter { it instanceof DasTable }.each { generate(it, dir) }
}

def generate(table, dir) {
    def tableName = table.getName();
    def className = javaName(tableName, true)
    def fields = calcFields(table)
    new File(dir, "${className}.java").withPrintWriter('utf-8') { out -> generateBean(out, className, table, fields) }
    new File(dir, "${className}Mapper.java").withPrintWriter('utf-8') { out -> generateMapper(out, className, table, fields) }
    new File(dir, "${className}Mapper.xml").withPrintWriter('utf-8') { out -> generateMapperXml(out, className, table, fields) }
    new File(dir, "${className}Service.java").withPrintWriter('utf-8') { out -> generateService(out, className, table, fields) }
    new File(dir, "${className}ServiceImpl.java").withPrintWriter('utf-8') { out -> generateServiceImpl(out, className, table, fields) }
    new File(dir, "${className}Controller.java").withPrintWriter('utf-8') { out -> generateController(out, className, table, fields) }
}

def generateBean(out, className, table, fields) {
    out.println "import cn.zyq.nutlet.system.common.BaseBean;"
    out.println "import io.swagger.annotations.ApiModelProperty;"
    out.println "import lombok.Data;"
    out.println ""
    out.println "import javax.persistence.*;"
    out.println "import java.io.Serializable;"
    out.println ""
    Set types = new HashSet()

    fields.each() {
        types.add(it.type)
    }
    out.println ""
    out.println "/**"
    out.println " * ${table.getComment()}"
    out.println " * ${new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}"
    out.println " */"
    out.println "@Entity"
    out.println "@Table(name =\"`${table.getName()}`\")"
    out.println "@Data"
    out.println "public class ${className} extends ${baseBeanName} implements Serializable {"
    out.println ""
    out.println genSerialID()
    fields.each() {
        def name = it.name;
        def comment = it.comment;
        def type = it.type;
        def isPK = it.isPK;
        if (isNotEmpty(name)) {
            if (!(name == "serviceID" || name == "createTime" || name == "updateTime" || name == "deleted" || name == "createBy" || name == "updateBy" || name == "auditReason" || name == "auditorID" || name == "auditTime")) {
                out.println ""
                // 输出注释
                if (isNotEmpty(comment)) {
                    out.println "\t/**"
                    out.println "\t * ${comment.toString()}"
                    out.println "\t */"
                }
                // 输出成员变量
                if (isPK) {
                    out.println "\t@Id"
                    out.println "\t@GeneratedValue(strategy = GenerationType.IDENTITY, generator = \"JDBC\")"
                }
                out.println "\t@Column(name = \"`${name}`\")"
                out.println "\t@ApiModelProperty(\"${comment}\")"
                if (type == "LocalDateTime") {
                    out.println "\t@JSONField(format = \"yyyy-MM-dd HH:mm:ss\")"
                }
//                if (isPK) {
//                    out.println "\tprivate ${type} id;"
//                } else {
//                    out.println "\tprivate ${type} ${name};"
//                }
                out.println "\tprivate ${type} ${name};"
            }
        }
    }
    out.println ""
    out.println "}"
}

def generateMapper(out, className, table, fields) {
    def tableName = table.getName();
//    def tableComment = table.getComment();

    out.println "import cn.zyq.beans.${tableName}.${className};"
    out.println "import org.springframework.stereotype.Component;"
    out.println "import cn.zyq.system.config.common.BaseMapper;"
    out.println ""
    out.println "import java.util.List;"
    out.println "import java.util.Map;"
    out.println ""
    out.println "/**"
    out.println " * ${table.getComment()}"
    out.println " * ${new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}"
    out.println " */"
    out.println "@Component"
    out.println "public interface ${className}Mapper extends BaseMapper<${className}> {"
    out.println "    List<${className}> list(Map<String, Object> param);"
    out.println "}"
}

def generateMapperXml(out, className, table, fields) {
    def tableName = table.getName();
    out.println "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
    out.println "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">"
    out.println "<mapper namespace=\"cn.zyq.mapper.${tableName}.${className}Mapper\">"
    out.println "    <select id=\"list\" resultType=\"cn.zyq.beans.${tableName}.${className}\">"
    out.println "        SELECT"
    fields.each() {
        out.println "            t.${it.name},"
    }
    out.println "        FROM"
    out.println "            ${tableName} t"
    out.println "        WHERE"
    out.println "            t.deleted = 0"
    out.println "    </select>"
    out.println "</mapper>"
}

def generateService(out, className, table, fields) {
    def tableName = javaName(className, false);

    out.println "import cn.zyq.beans.admin.Admin;"
    out.println "import cn.zyq.beans.${tableName}.${className};"
    out.println "import cn.zyq.system.common.PageParam;"
    out.println "import com.github.pagehelper.PageInfo;"
    out.println ""
    out.println "public interface ${className}Service {"
    out.println "    PageInfo<${className}> list(PageParam pageParam, Admin admin);"
    out.println ""
    out.println "    void add(${className} param, Admin admin);"
    out.println ""
    out.println "    void update(${className} param, Admin admin);"
    out.println ""
    out.println "    void del(String serviceID, Admin admin);"
    out.println ""
    out.println "    ${className} detail(String serviceID, Admin admin);"
    out.println ""
    out.println "    ${className} detail(String serviceID);"
    out.println "}"

}

def generateServiceImpl(out, className, table, fields) {
    def tableName = javaName(className, false);

    out.println "import cn.zyq.beans.admin.Admin;"
    out.println "import cn.zyq.beans.${tableName}.${className};"
    out.println "import cn.zyq.mapper.${tableName}.${className}Mapper;"
    out.println "import cn.zyq.service.${tableName}.${className}Service;"
    out.println "import cn.zyq.system.common.BaseService;"
    out.println "import cn.zyq.system.common.PageParam;"
    out.println "import org.springframework.beans.factory.annotation.Autowired;"
    out.println "import org.springframework.stereotype.Service;"
    out.println "import com.github.pagehelper.PageHelper;"
    out.println "import com.github.pagehelper.PageInfo;"
    out.println "import lombok.extern.slf4j.Slf4j;"
    out.println "import java.util.List;"
    out.println ""
    out.println "/**"
    out.println " * ${table.getComment()}"
    out.println " * ${new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}"
    out.println " */"
    out.println ""
    out.println "@Slf4j"
    out.println "@Service"
    out.println "public class ${className}ServiceImpl extends BaseService implements ${className}Service {"
    out.println "    @Autowired"
    out.println "    private ${className}Mapper ${tableName}Mapper;"
    out.println "    "
    out.println ""
    out.println "    @Override"
    out.println "    public PageInfo<${className}> list(PageParam pageParam, Admin admin) {"
    out.println "        PageHelper.startPage(pageParam.getPage(), pageParam.getPageSize());"
    out.println "        List<${className}> dbList = this.${tableName}Mapper.list(pageParam.getKv());"
    out.println "        return new PageInfo<>(dbList);"
    out.println "    }"
    out.println ""
    out.println "    @Override"
    out.println "    public void add(${className} param, Admin admin) {"
    out.println "        this.setCreateBaseData(param, admin);"
    out.println "        this.${tableName}Mapper.insertSelective(param);"
    out.println "    }"
    out.println ""
    out.println "    @Override"
    out.println "    public void update(${className} param, Admin admin) {"
    out.println "        Integer id = param.get${className}ID();"
    out.println "        this.setUpdateBaseData(param, admin);"
    out.println "        this.${tableName}Mapper.updateByPrimaryKeySelective(param);"
    out.println "    }"
    out.println ""
    out.println "    @Override"
    out.println "    public void del(String serviceID, Admin admin) {"
    out.println "        ${className} dbInfo = this.detail(serviceID);"
    out.println "        Integer id = dbInfo.get${className}ID();"
    out.println "        this.deleteLogic(id, admin, this.${tableName}Mapper);"
    out.println "    }"
    out.println ""
    out.println "    @Override"
    out.println "    public ${className} detail(String serviceID, Admin admin) {"
    out.println "        return this.detail(serviceID);"
    out.println "    }"
    out.println ""
    out.println "    @Override"
    out.println "    public ${className} detail(String serviceID) {"
    out.println "        ${className} dbInfo = this.getByServiceID(serviceID, ${className}.class, this.${tableName}Mapper);"
    out.println "        this.checkLogicExist(dbInfo);"
    out.println "        return dbInfo;"
    out.println "    }"
    out.println "}"
}

def generateController(out, className, table, fields) {
    def tableComment = table.getComment();
    def tableName = javaName(className, false);

    out.println "import cn.zyq.beans.${tableName}.${className};"
    out.println "import cn.zyq.service.${tableName}.${className}Service;"
    out.println "import cn.zyq.system.common.BaseController;"
    out.println "import cn.zyq.system.common.PageParam;"
    out.println "import io.swagger.annotations.Api;"
    out.println "import io.swagger.annotations.ApiOperation;"
    out.println "import org.springframework.web.bind.annotation.*;"
    out.println ""
    out.println "import javax.annotation.Resource;"
    out.println ""
    out.println "/**"
    out.println " * ${table.getComment()}"
    out.println " * ${new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}"
    out.println " */"
    out.println ""
    out.println "@Api(tags = \"${tableComment}\")"
    out.println "@RestController(value = \"Mgt${className}Controller\")"
    out.println "@RequestMapping(value = \"/mgt/${tableName}\")"
    out.println "public class ${className}Controller extends BaseController {"
    out.println ""
    out.println "    @Resource"
    out.println "    private ${className}Service ${tableName}Service;"
    out.println ""
    out.println "    @ApiOperation(value = \"列表\")"
    out.println "    @PostMapping(value = \"/list\")"
    out.println "    public Object list(@RequestBody PageParam pageParam) {"
    out.println "        return this.${tableName}Service.list(pageParam, this.getLoginAdmin());"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperation(value = \"添加\")"
    out.println "    @PostMapping(value = \"/add\")"
    out.println "    public void add(@RequestBody ${className} param) {"
    out.println "        this.${tableName}Service.add(param, this.getLoginAdmin());"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperation(value = \"修改\")"
    out.println "    @PostMapping(value = \"/update\")"
    out.println "    public void update(@RequestBody ${className} param) {"
    out.println "        this.${tableName}Service.update(param, this.getLoginAdmin());"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperation(value = \"详情\")"
    out.println "    @GetMapping(value = \"/detail/{serviceID}\")"
    out.println "    public Object detail(@PathVariable(value = \"serviceID\") String serviceID) {"
    out.println "        return this.${tableName}Service.detail(serviceID, this.getLoginAdmin());"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperation(value = \"删除\")"
    out.println "    @DeleteMapping(value = \"/del/{serviceID}\")"
    out.println "    public void del(@PathVariable(value = \"serviceID\") String serviceID) {"
    out.println "        this.${tableName}Service.del(serviceID, this.getLoginAdmin());"
    out.println "    }"
    out.println "}"
    out.println ""
}

def calcFields(table) {
    DasUtil.getColumns(table).reduce([]) { fields, col ->
        def spec = Case.LOWER.apply(col.getDataType().getSpecification())
        def typeStr = typeMapping.find { p, t -> p.matcher(spec).find() }.value
        def name = col.getName()
        def comm = [
                name   : name,
                type   : typeStr,
                comment: col.getComment(),
                isPK   : DasUtil.isPrimary(col)
        ]
        if (comm.isPK) {
            baseBeanName = "BaseBean"
        }
        if (name == "auditorID") {
            baseBeanName = "BaseAuditBean"
        }
        fields += [comm]
    }
}

def javaName(str, capitalize) {
    def s = com.intellij.psi.codeStyle.NameUtil.splitNameIntoWords(str)
            .collect { Case.LOWER.apply(it).capitalize() }
            .join("")
    capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

def isNotEmpty(content) {
    return content != null && content.toString().trim().length() > 0
}

static String genSerialID() {
    return "\tprivate static final long serialVersionUID = ${Math.abs(new Random().nextLong())}L;"
}
